/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.lestr.genver.data.serialization;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

/**
 *
 * @author PIBONNIN
 */
public class Reader {


    private ArrayList<Object> alreadyReadedObjects;


    private BeanSerializerAdapter defaultBeanSerializerAdapter;


    private byte[] buffer;


    private InputStream input;


    public Reader() {

        buffer = new byte[8];
        input = null;
        alreadyReadedObjects = new ArrayList<Object>();
        defaultBeanSerializerAdapter = new BeanSerializerAdapter();

    }// END Constructor


    public void open(InputStream input) throws IOException {

        close();

        this.input = input;

    }// FIN Method open


    public void close() throws IOException {

        alreadyReadedObjects.clear();
        input = null;

    }// FIN Method close


    public Object read(Class<?> type) throws IOException, InstantiationException, IllegalAccessException {

        if (type.getName().equals("java.lang.Float")
            || type.getName().equals("float"))
            return readFloat();
        else if (type.getName().equals("java.lang.Double")
                 || type.getName().equals("double"))
            return readDouble();
        else if (type.getName().equals("java.lang.Short")
                 || type.getName().equals("short"))
            return readShort();
        else if (type.getName().equals("java.lang.Integer")
                 || type.getName().equals("int"))
            return readInt();
        else if (type.getName().equals("java.lang.Long")
                 || type.getName().equals("long"))
            return readLong();
        else if (type.getName().equals("java.lang.Byte")
                 || type.getName().equals("byte"))
            return readByte();
        else if (type.getName().equals("java.lang.String"))
            return readString();
        else if (type.getName().equals("java.lang.Character")
                 || type.getName().equals("char"))
            return readChar();
        else if (type.getName().equals("java.lang.Boolean")
                 || type.getName().equals("boolean"))
            return readBoolean();
        else if (type.getName().equals("java.lang.float")
                 || type.getName().equals("void")) {

            readByte();
            return null;

        } else {

            int objectIndex = readInt();

            if (objectIndex == -1)
                return alreadyReadedObjects.get(objectIndex);
            else {

                Object rslt = type.newInstance();
                alreadyReadedObjects.add(rslt);

                if (ISerializable.class.isAssignableFrom(type))
                    ((ISerializable) rslt).load(this);
                else {
                    defaultBeanSerializerAdapter.setBeanSerializer(new DefaultBeanSerializer(type));
                    defaultBeanSerializerAdapter.read(this, rslt);
                }

                return rslt;

            }

        }

    }// END Method Read


    private byte readByte() throws IOException {

        input.read(buffer, 0, 1);

        return buffer[0];

    }// END Method readByte


    private boolean readBoolean() throws IOException {

        input.read(buffer, 0, 1);

        return buffer[0] != 0;

    }// END Method readBoolean


    private char readChar() throws IOException {

        input.read(buffer, 0, 2);

        return (char) ((buffer[1] & 0xFF) + ((buffer[0]) << 8));

    }// END Method readChar


    private short readShort() throws IOException {

        input.read(buffer, 0, 2);

        return (short) ((buffer[1] & 0xFF) + ((buffer[0]) << 8));

    }// END Method readShort


    private int readInt() throws IOException {

        input.read(buffer, 0, 4);

        return (buffer[3] & 0xFF)
               + ((buffer[2] & 0xFF) << 8)
               + ((buffer[1] & 0xFF) << 16)
               + ((buffer[0]) << 24);

    }// END Method readInt


    private float readFloat() throws IOException {

        input.read(buffer, 0, 4);

        int i = (buffer[3] & 0xFF)
                + ((buffer[2] & 0xFF) << 8)
                + ((buffer[1] & 0xFF) << 16)
                + ((buffer[0]) << 24);

        return Float.intBitsToFloat(i);

    }// END Method readFloat


    private long readLong() throws IOException {

        input.read(buffer, 0, 8);

        return (buffer[7] & 0xFFL)
               + ((buffer[6] & 0xFFL) << 8)
               + ((buffer[5] & 0xFFL) << 16)
               + ((buffer[4] & 0xFFL) << 24)
               + ((buffer[3] & 0xFFL) << 32)
               + ((buffer[2] & 0xFFL) << 40)
               + ((buffer[1] & 0xFFL) << 48)
               + (((long) buffer[0]) << 56);

    }// END Method readLong


    private double readDouble() throws IOException {

        input.read(buffer, 0, 8);

        long j = (buffer[7] & 0xFFL)
                 + ((buffer[6] & 0xFFL) << 8)
                 + ((buffer[5] & 0xFFL) << 16)
                 + ((buffer[4] & 0xFFL) << 24)
                 + ((buffer[3] & 0xFFL) << 32)
                 + ((buffer[2] & 0xFFL) << 40)
                 + ((buffer[1] & 0xFFL) << 48)
                 + (((long) buffer[0]) << 56);

        return Double.longBitsToDouble(j);

    }// END Method readDouble


    private String readString() throws IOException {

        int length = readInt();

        byte[] charBuffer = new byte[length];
        input.read(charBuffer, 0, length);

        return new String(charBuffer, "UTF-8");

    }// END Method readString


}// END Class bits


